package camid.main;

import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import camid.distribution.activeobject.ProxyBuilder;
import camid.distribution.broker.Requestor;
import camid.distribution.protocol.Endpoint;
import camid.infrastructure.SenderQueue;
import camid.services.nameservice.INameService;
import camid.services.nameservice.NameService;
import camid.services.nodemanager.ForwarderRequestorInterceptor;
import camid.test.testobjects.Calculator;
import camid.test.testobjects.ICalculator;

public class ClientFacade 
{
	private Requestor requestor;
	private SenderQueue senderQueue;
	private INameService nameService;
	
	private String sessionId;
	
	private AtomicInteger endpointIndex;
	
	private static ClientFacade INSTANCE;
	
	private ClientFacade()
	{
		this.endpointIndex = new AtomicInteger();
		this.senderQueue = new SenderQueue();
		this.requestor = new Requestor(senderQueue);
		this.sessionId = UUID.randomUUID().toString();
	}
	
	public static synchronized ClientFacade getInstance() 
	{
		if(INSTANCE == null)
		{
			INSTANCE = new ClientFacade();
		}
		return INSTANCE;
	}
	

	public INameService remoteNameService(String host, int port) {
		Endpoint ep = new Endpoint(host,port);
		ProxyBuilder<INameService> builder = new ProxyBuilder<INameService>(
				INameService.class,
				sessionId, NameService.NAME_SERVICE_ID, NameService.class, 
				requestor, ep);
		INameService ns = builder.newProxy();
		this.setNameService(ns);
		this.requestor.addInterceptor(new ForwarderRequestorInterceptor(this.nameService, requestor));
		return ns;
	}
	
	public Requestor getRequestor() {
		return requestor;
	}

	public void setRequestor(Requestor requestor) {
		this.requestor = requestor;
	}

	public INameService getNameService() {
		return nameService;
	}

	public void setNameService(INameService nameService) {
		this.nameService = nameService;
	}
	
	public List<Endpoint> lookupEndpointsForGivenClass(Class<?> entityClass)
	{
		List<Endpoint> eps = this.nameService.getObjectDefinitionEndpoints(entityClass.getCanonicalName());
		return eps;
	}
	
	public <E> E lookupObject(Class<E> iface, Class<?> entityClass, String objectId, Endpoint ep)
	{	
		ProxyBuilder<E> builder = new ProxyBuilder<E>(
				iface,
				sessionId, objectId, entityClass, 
				requestor, ep);
		
		E entityProxy = builder.newProxy();
		
		return entityProxy;
	}
	
	public <E> E lookupObject(Class<E> iface, Class<?> entityClass, String objectId)
	{
		Endpoint ep = null;
		for(int i = 0; (i < 3) && (ep == null) ; i++)
		{
			try 
			{
				ep = this.nameService.lookup(objectId);
				
				if(ep == null)
				{
					List<Endpoint> eps = this.nameService.getObjectDefinitionEndpoints(entityClass.getCanonicalName());
					if(eps.size() > 0)
					{
						int pos = endpointIndex.incrementAndGet() % eps.size();
						ep = eps.get(pos);
					}
				}
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
			if(ep == null)
			{
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		
		
		ProxyBuilder<E> builder = new ProxyBuilder<E>(
				iface,
				sessionId, objectId, entityClass, 
				requestor, ep);
		
		E entityProxy = builder.newProxy();
		
		return entityProxy;
	}
	
	
	
	
	
	public static void main(String[] args) 
	{
		String nsHost = null;
		Integer nsPort = null;
		
		for(int i = 0; i < args.length; i++)
		{
			String arg = args[i];
			
			if(arg.equals("-nsport") && i < (args.length - 1))
			{
				try
				{
					nsPort = Integer.parseInt(args[i+1]);
				}
				catch(NumberFormatException e)
				{
					e.printStackTrace();
				}
				
			}
			if(arg.equals("-nshost") && i < (args.length - 1))
			{
				nsHost = args[i+1];
			}
		}
		
		final ClientFacade facade = ClientFacade.getInstance();
		facade.remoteNameService(nsHost, nsPort.intValue());
		
		final AtomicInteger index = new AtomicInteger(0);
		ExecutorService executor = Executors.newCachedThreadPool();
		
		final Random random = new Random();
		
		for(int i = 0; i < 10; i++)
		{
			executor.submit(new Runnable()
			{
				@Override
				public void run() 
				{	
					ICalculator calc = facade.lookupObject(ICalculator.class, Calculator.class, 
							"calc" + index.getAndIncrement());
					for(int j = 0; j < 1000; j++)
					{
						try
						{
							Thread.sleep(2000);
							System.out.println(calc.sum( random.nextInt(), random.nextInt() ));
						}
						catch(Exception e)
						{
							e.printStackTrace();
						}
						
					}
				}
				
			});
		}
		
		executor.shutdown();
		
	}
	
}
